package middlewareconf

import (
	"context"
	"fmt"
	"strings"
	"sync"

	"gitee.com/xfrm/middleware/xconfig"
	"gitee.com/xfrm/middleware/xcontext"
)

var (
	middlewareConfig *MiddleConf
	mcLock           sync.Mutex
)

// MiddleConf middleware apollo config
type MiddleConf struct {
	config xconfig.ConfigCenter
}

// New ...
func New(ctx context.Context) (*MiddleConf, error) {
	config, err := xconfig.NewConfigCenter(ctx,
		xconfig.ConfigTypeApollo, "",
		xconfig.DefaultMiddlewareService,
		[]string{xconfig.DefaultMQNamespace, xconfig.DefaultCacheNamespace,
			defaultNewCacheNamespace, defaultRedisPasswordNamespace})
	if err != nil {
		return nil, err
	}
	return &MiddleConf{
		config: config,
	}, nil
}

// RegisterObserver register observer to apollo config, and start watch
func (p *MiddleConf) RegisterObserver(ctx context.Context, applyChangeEvent xconfig.EventHandFunc) {
	p.config.RegisterObserver(ctx, xconfig.NewConfigObserver(applyChangeEvent))
}

// Close ...
func (p *MiddleConf) Close() {
	p.config.Stop(context.Background())
}

func (p *MiddleConf) getConfigStringItemWithFallback(ctx context.Context, key string, name string, mType MiddlewareType) (string, bool) {
	namespace := getNamespaceByType(mType)
	val, ok := p.config.GetStringWithNamespace(ctx, namespace, p.buildConfigKey(ctx, key, name, mType))
	if !ok {
		defaultCtx := context.WithValue(ctx, xcontext.ContextKeyControl, simpleContextControlRouter{defaultRouteGroup})
		val, ok = p.config.GetStringWithNamespace(defaultCtx, namespace, p.buildConfigKey(defaultCtx, key, name, mType))
	}
	return val, ok
}

func (p *MiddleConf) getConfigIntItemWithFallback(ctx context.Context, key, name string, mType MiddlewareType) (int, bool) {
	namespace := getNamespaceByType(mType)
	val, ok := p.config.GetIntWithNamespace(ctx, namespace, p.buildConfigKey(ctx, key, name, mType))
	if !ok {
		defaultCtx := context.WithValue(ctx, xcontext.ContextKeyControl, simpleContextControlRouter{defaultRouteGroup})
		val, ok = p.config.GetIntWithNamespace(defaultCtx, namespace, p.buildConfigKey(defaultCtx, key, name, mType))
	}
	return val, ok
}

func (p *MiddleConf) getConfigBoolItemWithFallback(ctx context.Context, key, name string, mType MiddlewareType) (bool, bool) {
	namespace := getNamespaceByType(mType)
	val, ok := p.config.GetBoolWithNamespace(ctx, namespace, p.buildConfigKey(ctx, key, name, mType))
	if !ok {
		defaultCtx := context.WithValue(ctx, xcontext.ContextKeyControl, simpleContextControlRouter{defaultRouteGroup})
		val, ok = p.config.GetBoolWithNamespace(defaultCtx, namespace, p.buildConfigKey(defaultCtx, key, name, mType))
	}
	return val, ok
}

func (p *MiddleConf) buildConfigKey(ctx context.Context, key, item string, mType MiddlewareType) string {
	return strings.Join([]string{
		key,
		xcontext.GetControlRouteGroupWithDefault(ctx, defaultRouteGroup),
		fmt.Sprint(mType),
		item,
	}, apolloConfigSep)
}

// GetMiddlewareConfig get middleware global var config
func GetMiddlewareConfig() (*MiddleConf, error) {
	mcLock.Lock()
	defer mcLock.Unlock()

	if middlewareConfig != nil {
		return middlewareConfig, nil
	}

	mc, err := New(context.Background())
	if err != nil {
		return nil, err
	}
	middlewareConfig = mc
	return middlewareConfig, nil
}

type simpleContextControlRouter struct {
	group string
}

func (s simpleContextControlRouter) GetControlRouteGroup() (string, bool) {
	return s.group, true
}

func (s simpleContextControlRouter) SetControlRouteGroup(group string) error {
	s.group = group
	return nil
}
